package com.polymathcoder.dpastry.core.config;

import com.polymathcoder.dpastry.api.config.Configuration;
import com.polymathcoder.dpastry.api.config.ConfigurationException;
import com.polymathcoder.dpastry.dPastry;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;

import java.io.File;
import java.io.InputStream;

/**
 * XML-serializable dPastry Core Configuration Class.
 *
 * @author Drew Botwinick
 * @version 0.0.1
 */
@Root(name = "config")
public final class CoreConfiguration implements Configuration<dPastry> {

    // *************** PRIVATE FIELDS ***************

    @Element(name = "services")
    private ServiceOptions serviceOptions = new ServiceOptions();

    @Element(name = "processing-pool")
    private ThreadPool processingPool = new ThreadPool();

    @Element(name = "io-pool")
    private ThreadPool ioPool = new ThreadPool();

    // *************** ACCESSOR METHODS ***************

    public ServiceOptions serviceOptions() {
        return serviceOptions;
    }

    public ThreadPool processingPool() {
        return processingPool;
    }

    public ThreadPool ioPool() {
        return ioPool;
    }

    // *************** SUB-CONFIGURATION CLASSES ***************

    @Root
    public static class ThreadPool {
        @Element(name = "minimum")
        private int corePoolSize = 2;

        @Element(name = "maximum")
        private int maxPoolSize = 8;

        @Element(name = "alive")
        private long keepAliveTime = 60;

        protected ThreadPool() {}

        public void setCorePoolSize(int corePoolSize) {
            this.corePoolSize = corePoolSize;
        }

        public void setMaxPoolSize(int maxPoolSize) {
            this.maxPoolSize = maxPoolSize;
        }

        public void setKeepAliveTime(long keepAliveTime) {
            this.keepAliveTime = keepAliveTime;
        }

        public int getCorePoolSize() {
            return corePoolSize;
        }

        public int getMaxPoolSize() {
            return maxPoolSize;
        }

        public long getKeepAliveTime() {
            return keepAliveTime;
        }
    }

    @Root
    public static class ServiceOptions {
        @Element(name = "start-timeout")
        private int startTimeout = 5000;

        @Element(name = "stop-timeout")
        private int stopTimeout = 5000;

        protected ServiceOptions() {}

        public void setStartTimeout(int startTimeout) {
            this.startTimeout = startTimeout;
        }

        public void setStopTimeout(int stopTimeout) {
            this.stopTimeout = stopTimeout;
        }

        public int getStartTimeout() {
            return startTimeout;
        }

        public int getStopTimeout() {
            return stopTimeout;
        }
    }

    // *************** STATIC METHODS ***************

    /**
     * Static method to return the default configuration. This can be used as the starting point when only minor changes
     * are being made to the configuration. It is recommended that significantly different configurations be stored in
     * custom xml files.
     *
     * @return a CoreConfiguration object containing the default configuration
     * @throws ConfigurationException throws a ConfigurationException if anything goes wrong
     */
    public static CoreConfiguration getDefaultConfiguration() throws ConfigurationException {
        return fromInputStream(CoreConfiguration.class.getResourceAsStream("CoreConfiguration.xml"));
    }

    /**
     * Static method to return the configuration specified by the target of the given File object.
     *
     * @param f the file object from which to load the configuration XML
     * @return a CoreConfiguration object containing the specified configuration data
     * @throws com.polymathcoder.dpastry.api.config.ConfigurationException
     *          throws a ConfigurationException if anything goes wrong
     */
    public static CoreConfiguration fromFile(File f) throws ConfigurationException {
        Serializer serializer = new Persister();
        try {
            return serializer.read(CoreConfiguration.class, f);
        } catch (Exception e) {
            throw new ConfigurationException(e);
        }
    }

    /**
     * Static method to return the configuration specified by the target of the given InputStream.
     * This method is provided as a convenience so that one can use Class.getResourceAsStream(...) as
     * the source of the configuration XML.
     *
     * @param s the InputStream from which to load the configuration XML
     * @return a CoreConfiguration object containing the specified configuration data
     * @throws ConfigurationException throws a ConfigurationException if anything goes wrong
     */
    public static CoreConfiguration fromInputStream(InputStream s) throws ConfigurationException {
        Serializer serializer = new Persister();
        try {
            return serializer.read(CoreConfiguration.class, s);
        } catch (Exception e) {
            throw new ConfigurationException(e);
        }
    }

    /**
     * Static method to save the given CoreConfiguration to a File object.
     *
     * @param cfg the CoreConfiguration to save
     * @param f   the target File
     * @return the saved CoreConfiguration if successful
     * @throws ConfigurationException throws a ConfigurationException if anything goes wrong
     */
    public static CoreConfiguration toFile(CoreConfiguration cfg, File f) throws ConfigurationException {
        Serializer serializer = new Persister();
        try {
            serializer.write(cfg, f);
            return cfg;
        } catch (Exception e) {
            throw new ConfigurationException(e);
        }
    }

}
